# -*- coding: utf-8 -*-
from clang import cindex as ci
import logging
from common import walk_ast_tree, NDW_DEBUG, trace_method, logable


__author__ = 'sysprog'
_TRACE = 5


def unused_func_visitor_callback(node, parent, **kwargs):
    import clang.cindex as ci
    func_list = kwargs["func_list"]
    if node.kind == ci.CursorKind.FUNCTION_DECL:
        if node.location.file.name.startswith("/usr"):
            logging.log(_TRACE, "%s from /usr is ignored", node.spelling)
            return 1
        sign = FunctionSignature.from_node(node)

        func_list.append(sign)
    elif node.kind == ci.CursorKind.CALL_EXPR:
        node_def = node.get_definition()
        if node_def is None:
            logging.log(_TRACE, "library member '%s'", node.displayname)
            node_def = node.referenced
        if node_def is not None:
            func_list.append(FunctionSignature.from_node(node_def))
    return 0

@logable
class FunctionsChecker31(object):
    def __init__(self):
        self.in_both = []
        self.only_in_sign = []
        self.only_in_func = []

    @trace_method
    def check_functions(self, tu, signs):
        from clang.cindex import TranslationUnit
        assert(type(tu) is TranslationUnit)

        signature_list = []
        for line in signs.split("\n"):
            fs = FunctionSignature.from_line(line)
            signature_list.append(FunctionSignature.from_line(line))

        func_list = []
        walk_ast_tree(tu.cursor, tu.cursor, unused_func_visitor_callback, func_list=func_list)

        sign_set = set(signature_list)
        func_set = set(func_list)

        self.in_both = list(it for it in sign_set.intersection(func_set))
        self.only_in_sign = list(it for it in sign_set.difference(func_set))
        self.only_in_func = list(it for it in func_set.difference(sign_set))

        self.log(NDW_DEBUG, "self.in_both = %s", self.in_both)
        self.log(NDW_DEBUG, "self.only_in_sign = %s", self.only_in_sign)
        self.log(NDW_DEBUG, "self.only_in_func = %s", self.only_in_func)


class FunctionSignature(object):
    def __init__(self, func_name, func_type, name_with_arg, **kwargs):
        self.func_name = func_name
        self.func_type = func_type
        self.name_with_arg = name_with_arg

    @staticmethod
    def from_line(line):
        """
            line example:
                'int main(int, char **)'
        """
        tp, diplayname = line.rstrip().split(" ", 1)
        fname = diplayname.split("(")[0]
        return FunctionSignature(fname, tp, diplayname)

    @staticmethod
    def from_node(nd):
        func_name = nd.spelling
        func_type = nd.result_type.spelling
        diplayname = nd.displayname
        return FunctionSignature(func_name, func_type, diplayname)


    def __str__(self):
        return "%(type)s %(dsplname)s" % {'type': self.func_type, 'dsplname': self.name_with_arg}

    def __repr__(self):
        return "FunctionSignature'" + str(self) + "'"

    """
        __eq__ and __hash__ are need for set() support
    """
    def __eq__(self, other):
        return self.func_name == other.func_name

    def __hash__(self):
        return hash(self.func_name)